package com.ymx.darling.executor.mapping.result;

import com.ymx.darling.bean.entity.BeanInfo;
import com.ymx.darling.bean.entity.BeanInfoManager;
import com.ymx.darling.bean.entity.FieldInfo;
import com.ymx.darling.bean.sql.Statement;
import com.ymx.darling.build.mapper.dynamic.SqlSourceRepository;
import com.ymx.darling.build.mapper.dynamic.result.RelationNode;
import com.ymx.darling.build.mapper.dynamic.result.ResultMapNode;
import com.ymx.darling.build.mapper.dynamic.result.ResultNode;
import com.ymx.darling.exception.EntityException;
import com.ymx.darling.exception.ExceptionFactory;
import com.ymx.darling.exception.ResultMappingException;
import com.ymx.darling.executor.mapping.metadata.MetaData;
import com.ymx.darling.executor.mapping.metadata.MetaDataManager;
import com.ymx.darling.executor.mapping.relation.RelationFieldInfo;
import com.ymx.darling.type.TypeHandler;
import com.ymx.darling.util.StringUtil;

import java.util.*;

/**
 * @author 爱Java的小于
 */
public class ManualResultMapBuilder implements ResultMapBuilder{
    private Statement statement;

    public ResultMap buildResultMap(Statement statement, LinkedHashSet<MetaData> metaDataList) {
        if (metaDataList.isEmpty()) {
            return null;
        } else {
            try {
                // 方便上下文查找
                this.statement = statement;
                // 转换为map方便查找
                Map<String, MetaData> metaDataMap = new HashMap<>();
                metaDataList.forEach(metaData -> metaDataMap.put(metaData.getName(), metaData));

                return this.createResultMap(statement.getResultMapNode(), metaDataMap);
            } catch (Exception exception) {
                ResultMappingException resultMappingException = new ResultMappingException(exception, String.format("构建id为'%s'的ResultMap时抛出异常.原因:%s:%s",
                        statement.getResultMapNode().getId(), exception.getClass().getName(), exception.getMessage()));
                throw ExceptionFactory.throwStrategy(exception, resultMappingException);
            }
        }
    }


    /**
     * 根据xml的ResultMap设置映射
     * @param resultMapNode resultMapNode节点
     * @param metaDataMap 结果集元数据集合
     * @return ResultMap
     */
    public ResultMap createResultMap(ResultMapNode resultMapNode, Map<String, MetaData> metaDataMap) throws NoSuchFieldException {
        // 返回映射类型的BeanInfo
        BeanInfo beanInfo = BeanInfoManager.getBeanInfo(resultMapNode.getType());
        // 根据result映射创建resultMap
        ResultMap resultMap = this.createByResult(beanInfo, resultMapNode.getResultNodes(), metaDataMap);
        // 判断映射relation节点
        if (resultMap != null && !resultMapNode.getRelationNodes().isEmpty()) {
            for (RelationNode relationNode : resultMapNode.getRelationNodes()) {
                RelationFieldInfo relationFieldInfo = this.getRelationFieldInfo(beanInfo, relationNode.getProperty());
                // 解析关联字段
                ResultMap childResultMap = this.createByRelation(relationNode, relationFieldInfo, metaDataMap);
                if (childResultMap != null) {
                    resultMap.addResultMaps(childResultMap, relationFieldInfo.getAssociationType());
                }
            }
        }
        return resultMap;
    }


    /**
     * 根据简单类型字段映射构建ResultMap
     * @param beanInfo beanInfo
     * @param resultNodes result节点集合
     * @param metaDataMap 结果集元数据
     * @return ResultMap
     */
    public ResultMap createByResult(BeanInfo beanInfo, List<ResultNode> resultNodes, Map<String, MetaData> metaDataMap) throws NoSuchFieldException {
        Map<String, FieldInfo> simpleTypeColumnMap = beanInfo.getSimpleColumnMap();
        Map<String, FieldInfo> fieldMap = beanInfo.getFieldMap();
        List<MetaData> metaDataList = new ArrayList<>();
        MetaData idMetaData = null;
        for (ResultNode resultNode : resultNodes) {
            FieldInfo fieldInfo = fieldMap.get(resultNode.getProperty());
            if (fieldInfo == null) {
                throw new NoSuchFieldException("找不到属性:"+resultNode.getProperty());
            }
            if (!simpleTypeColumnMap.containsValue(fieldInfo)) {
                throw new EntityException("属性"+resultNode.getProperty()+"不能进行简单类型的映射.该字段可能是关联类型字段或业务字段");
            }
            MetaData metaData = metaDataMap.get(resultNode.getColumn());
            if (metaData == null) {
                throw new ResultMappingException("结果集找不到字段:"+resultNode.getColumn());
            }
            TypeHandler<?> typeHandler = fieldInfo.getTypeHandler();
            if (typeHandler != null) {
                metaData.setTypeHandler(typeHandler);
            }
            // 判断是id字段
            if (fieldInfo.equals(beanInfo.getIdColumnInfo())) {
                idMetaData = metaData;
            }
            metaData.setField(fieldInfo.getField());
            metaDataList.add(metaData);
        }

        if (metaDataList.isEmpty()) {
            return null;
        } else {
            // 添加类型处理器
            MetaDataManager.addTypeHandler(metaDataList);

            ResultMap resultMap = new ResultMap();
            resultMap.setBeanInfo(beanInfo);
            resultMap.setIdMetaData(idMetaData);
            resultMap.setModule(beanInfo.getModule());
            resultMap.setMetaDataList(metaDataList);
            return resultMap;
        }
    }


    /**
     * 处理关联Relation节点内容
     * @param relationNode relation节点
     * @param relationFieldInfo relation字段的封装类
     * @param metaDataMap 结果集元数据
     * @return ResultMap
     * @throws NoSuchFieldException 获取关联字段不存在时会抛出此异常
     */
    public ResultMap createByRelation(RelationNode relationNode, RelationFieldInfo relationFieldInfo, Map<String, MetaData> metaDataMap) throws NoSuchFieldException {
        // 根据result映射创建resultMap
        BeanInfo beanInfo = BeanInfoManager.getBeanInfo(relationFieldInfo.getFieldType());
        ResultMap resultMap;
        if (StringUtil.isNoNull(relationNode.getResultMap())) {
            String resultMapId = relationNode.getResultMap();
            SqlSourceRepository sqlSourceRepository = statement.getConfiguration().getSqlSourceRepository();
            if (sqlSourceRepository.hasResultMap(statement.getNamespace(), resultMapId)) {
                ResultMapNode relationResultMapNode = sqlSourceRepository.getResultMap(statement.getNamespace(), resultMapId);
                resultMap = this.createResultMap(relationResultMapNode, metaDataMap);
            } else {
                throw new ResultMappingException("关联属性节点'"+relationNode.getProperty()+"'的resultMaps属性'"+resultMapId+"'不存在");
            }
        } else {
            resultMap = this.createByResult(beanInfo, relationNode.getResultNodes(), metaDataMap);
        }
        // 判断映射relation节点
        List<RelationNode> relationNodes = relationNode.getRelationNodes();
        if (resultMap != null && !relationNodes.isEmpty()) {
            for (RelationNode childRelationNode : relationNodes) {
                RelationFieldInfo childRelationFieldInfo = this.getRelationFieldInfo(beanInfo, childRelationNode.getProperty());
                ResultMap childResultMap = this.createByRelation(childRelationNode, childRelationFieldInfo, metaDataMap);
                if (childResultMap != null) {
                    resultMap.addResultMaps(childResultMap, childRelationFieldInfo.getAssociationType());
                }
            }
        }

        // 设置关联字段的Field实例
        if (resultMap != null) {
            resultMap.setField(relationFieldInfo.getField());
        }

        return resultMap;
    }


    /**
     * 返回关联的实体的字段
     * @param beanInfo beanInfo
     * @param relationProperty 关联字段名称
     * @return RelationFieldInfo
     */
    private RelationFieldInfo getRelationFieldInfo(BeanInfo beanInfo, String relationProperty) throws NoSuchFieldException {
        RelationFieldInfo relationFieldInfo = null;
        for (RelationFieldInfo relationField : beanInfo.getRelationFieldInfoList()) {
            if (relationField.getFieldName().equals(relationProperty)) {
                relationFieldInfo = relationField;
                break;
            }
        }
        // 设置的关联映射字段不存在 抛出异常
        if (relationFieldInfo == null) {
            throw new NoSuchFieldException(String.format("类'%s'中不存在关联属性'%s'", beanInfo.getModule().getName(), relationProperty));
        }
        return relationFieldInfo;
    }
}
